/**
	Pulsar engine.
	Additional include file. File manager. In memory file streams.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

/*
*/
class StreamReadMemory : public StreamRead
{
public:
	// ctor with memory mapped file
	explicit StreamReadMemory(const pl::Filepath& fileName);
	// ctor with memory buffer
	explicit StreamReadMemory(const void* buf, pl_int64 size, StreamFreeMode mode);
	// ctor bu default
	explicit StreamReadMemory();
	// dtor
	virtual ~StreamReadMemory();

	/// Set reading data from memory buffer
	void set(const void* buf, pl_int32 size, StreamFreeMode mode);

	/// Set reading data from memory mapped file
	bool set(const pl::Filepath& fileName);

	/// Set LE/BE swap
	void setEndianSwap(bool bSet)
	{
		_bEndianSwap = bSet;
	}

	/// Check stream initialization
	virtual bool isInited() const
	{
		return (_buf != NULL) ? true : false;
	}

	/// Query file size
	virtual pl_int64 getFileSize() const
	{
		return _size;
	}

	/// Query current file point
	virtual pl_int64 tell() const
	{
		return _pos;
	}

	/// Change current file pointer
	virtual pl_int64 seek(pl_int64 pos, SeekInfo where);

	/// Check End of File
	virtual bool isEOF() const
	{
		return _pos >= _size;
	}

	/// Reading data
	virtual bool readBuffer(void* pBuf, pl_int32 itemSize, pl_int32 itemCount, pl_int64* lengthRead = NULL);

	/// Reading string
	virtual bool readString(pl_char* pStr, int maxLen, pl_char termChar = PL_T('\0'));

	/// Convert current infile value as offset to another part of memory mapped file
	template <class T> T* restorePointer(const T* ptr)
	{
		pl_intptr pos = reinterpret_cast<pl_intptr>(ptr);

		if (pos == 0)
		{
			return NULL;
		}
		PL_ASSERTE(pos >= 0 && pos < _size, PL_T("Pointer address outside file area"));
		return reinterpret_cast<T*>(_buf + pos);
	}

	/// Reads next value from stream as pointer
	template <class T> T* restoreTell(bool seekToPointer = false, pl_int32 sizeOffset = 0)
	{
		T* ptr;
		pl_intptr pos;
		read(ptr);
		pos = (pl_intptr)ptr;
		ptr = restorePointer(ptr);
		if (seekToPointer)
		{
			PL_ASSERTW(pos < 0x7FFFFFFF, PL_T("Seek overflow"));
			seek((pl_int32)pos, pl::FILE_SEEK_BEGIN);
		}
		if (sizeOffset > 0)
		{
			seek(sizeOffset, pl::FILE_SEEK_CURRENT);
		}
		return ptr;
	}

	/// Cobverts current tell() value as offset, seek in file automatically
	template <class T> T* convertTell(int sizeOffset, int reqAlign = sizeof(void*))
	{
		PL_ASSERTE(tell() < _size, PL_T("Pointer address outside file area"));

		T* ret = reinterpret_cast<T*>(_buf + tell());
		if (reqAlign > 0)
		{
			PL_ASSERTW(((pl_intptr)_buf + tell()) % reqAlign == 0, PL_T("Bad align"));
		}
		seek(sizeOffset, pl::FILE_SEEK_CURRENT);
		return ret;
	}

private:
	void _release();
	const pl_uint8* _buf;
	pl_int64 _size;
	pl_int64 _pos;
	StreamFreeMode _mode;
	bool _bEndianSwap;
};

/*
*/
class StreamWriteMemory : public StreamWrite
{
public:
	explicit StreamWriteMemory(pl_int32 initialSize, pl_int32 growSize, const pl::Filepath* fileNameSave = NULL);
	virtual ~StreamWriteMemory();

	virtual void disableSave()
	{
		_bEnableAutosave = false;
	}

	void setEndianSwap(bool bSet)
	{
		_bEndianSwap = bSet;
	}

	virtual bool isInited() const
	{
		return (_buf != NULL) ? true : false;
	}

	virtual pl_int64 getFileSize() const
	{
		return _size;
	}

	virtual bool setFileSize(pl_int64 newSize)
	{
		if (_size > newSize)
		{
			_size = newSize;
			_pos = pl::max(_pos, _size);
			return true;
		}
		return _checkExpand(newSize);
	}

	virtual int getMaxAlign() const
	{
		return _maxAlignFactor;
	}

	virtual pl_int64 tell() const
	{
		return _pos;
	}

	virtual pl_int64 seek(pl_int64 pos, SeekInfo where);

	virtual bool flush();

	virtual bool writeBuffer(const void* pBuf, pl_int32 itemSize, pl_int32 itemCount);

	pl::FileError saveFile(const pl::Filepath& fileName) const;
	pl::FileError saveFile(pl::StreamWrite* s) const;

	inline const void* getContent() const
	{
		return _buf;
	}

	void reset(const pl::Filepath* fileNameSave = NULL);

	template <class T> T* convertTell()
	{
		PL_ASSERTW(tell() % sizeof(void*) == 0, PL_T("Bad align"));
		return reinterpret_cast<T*>((pl_intptr)(tell()));
	}

private:
	bool _checkExpand(pl_int64 newSize);

	pl_int64 _size;
	pl_int32 _growSize;
	pl_int64 _capacity;
	pl_uint8* _buf;
	pl_int64 _pos;
	int _maxAlignFactor;
	pl::Filepath _defaultFileName;
	bool _bEnableAutosave;
	bool _bEndianSwap;
};

} // namespace pl

//
// End of file 'pulsar_file_stream_mem.h'
//
